/**
 * @file        dmem_log.c
 * @author      Shadow3D (ShadowThreeD@outlook.com)
 * @brief       
 * @version     0.1
 * @date        2022-01-19
 * 
 * @copyright   Copyright (c) 2022
 * 
 */
#include "./dmem_log.h"

#define ASSERT                  assert
#define PRINTF                  printf 
#define OS_ENTER_CRITICAL()                     /* ucos系统函数，进入临界区，即关闭中断 */
#define OS_ENTER_CRITICAL()                     /* ucos函数，退出临界区，即开中断 */
  

static DMEM_LOG *s_pstFreeLog;
static INT8U s_byNumUsedLog;
static DMEM_LOG *s_pstHeadLog;

#define NUM_DMEM_LOG    20
static DMEM_LOG s_astDMemLog[NUM_DMEM_LOG];

/**
 * @brief       
 * 
 */
void InitDMemLog(void)
{
    INT16S  nCnt;
    /* Initialize pool of log */
    for(nCnt = 1; nCnt < NUM_DMEM_LOG; ++nCnt)
    {
        s_astDMemLog[nCnt - 1].p_stNext = &s_astDMemLog[nCnt];
    }
    s_astDMemLog[NUM_DMEM_LOG - 1].p_stNext = NULL;
    s_pstFreeLog = &s_astDMemLog[0];    /* Point to the 1st log */
    return;
}

/**
 * @brief       
 * 
 * @param[in]   p_vAddr 
 * @param[in]   iSize 
 */
static void LogDMem(const void *p_vAddr, INT32S iSize)
{
    ASSERT(p_vAddr && iSize > 0);
    DMEM_LOG *p_stLog;
    #if OS_CRITICAL_METHOD == 3
    OS_CPU_SR cpu_sr;
    #endif

    /* Get a log from free pool */
    // OS_ENTER_CRITICAL();    /* Avoid race condition on s_pstFreeLog */  
    if(!s_pstFreeLog)
    {
        // OS_EXIT_CRITICAL();     
        PRINTF("Allocate DMemLog failed.\r\n");
        return;
    }
    p_stLog = s_pstFreeLog;
    s_pstFreeLog = s_pstFreeLog->p_stNext;
    // OS_EXIT_CRITICAL();

    /* Don't need to protect this log that is free one currently */
    p_stLog->p_vDMem = p_vAddr;
    p_stLog->iSize = iSize;

    /* Put this log into used chain */
    OS_ENTER_CRITICAL();    /* Avoid race condition */
    p_stLog->p_stNext = s_pstHeadLog;
    s_pstHeadLog = p_stLog;
    ++s_byNumUsedLog;
    // OS_EXIT_CRITICAL();

    return;
}

/**
 * @brief       Remove an allocated memory from log pool
 * 
 * @param[in]   p_vAddr const void* point to address of this allocated memory by this pointer
 */
static void UnlogDMem(const void *p_vAddr)
{
    ASSERT(p_vAddr);
    DMEM_LOG *p_stLog, *p_stPrev;
    #if OS_CRITICAL_METHOD == 3
    OS_CPU_SR cpu_sr;
    #endif

    /* Search the log */
    OS_ENTER_CRITICAL();    /* Avoid race condition */
    p_stLog = p_stPrev = s_pstHeadLog;
    while(p_stLog)
    {
        if(p_vAddr == p_stLog->p_vDMem)
        {
            break;      /* Have found */
        }  
        p_stPrev = p_stLog;
        p_stLog = p_stLog->p_stNext;
    }

    if(!p_stLog)
    {
        // OS_EXIT_CRITICAL();
        PRINTF("Search Log failed.\r\n");
        return;
    }

    /* Remove from used pool */
    if(p_stLog == s_pstHeadLog)
    {
        s_pstHeadLog = s_pstHeadLog->p_stNext;
    } else {
        p_stPrev->p_stNext = p_stLog->p_stNext;
    }
    --s_byNumUsedLog;
    // OS_EXIT_CRITICAL();

    /* Don't need to protect this log that is free one currently */
    p_stLog->p_vDMem = NULL;
    p_stLog->iSize = 0;

    /* Add info free pool */
    OS_ENTER_CRITICAL();    /* Avoid race condition */
    p_stLog->p_stNext = s_pstFreeLog;
    s_pstFreeLog = p_stLog;
    // OS_EXIT_CRITICAL();

    return;
}

/**
 * @brief       Malloc a block of memory and log it if need
 * 
 * @param[in]   iSize 
 * @return      void* 
 */
void *MallocExt(INT32S iSize)
{
    ASSERT(iSize > 0);      /* if true, do nothing; if false, print and stop running. */
    void *p_vAddr;

    p_vAddr = malloc(iSize);
    if(!p_vAddr)
    {
        PRINTF("malloc failed at %s line %d.\r\n", __FILE__, __LINE__);
    } else {
        #if (DMEM_DBG && DBG_VER)
        memset(p_vAddr, 0xA3, iSize);   /* Fill gargage for debug */
        LogDMem(p_vAddr, iSize);        /* Log memory for debug */
        #endif
    }
    return p_vAddr;
}

/**
 * @brief       Free a block of memory and unlog it if need
 * 
 * @param[in]   p_vMem point to the memory by this pointer
 */
void FreeExt(void *p_vMem)
{
    ASSERT(p_vMem);

    free(p_vMem);
    #if (DMEM_DBG && DBG_VER)
    UnlogDMem(p_vMem);      /* Remove memory form log */
    #endif
    return;
}

void PrintDMem(void)
{
    INT8U i = 0;

    do {
        printf("s_astDMemLog[%d]:\r\n", i);
        printf("  p_vDMem: %s\r\n", (char*)s_astDMemLog[i].p_vDMem);
        printf("  size: %d\r\n", s_astDMemLog[i].iSize);
    } while(s_astDMemLog[i++].p_stNext == NULL);
}